package fr.lip6.meta.ple.generation.aspectj;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import artefact.generic.ConstructionPrimitive;
import artefact.umlClassDiagram.CreateAssociation;
import artefact.umlClassDiagram.CreateAttribute;
import artefact.umlClassDiagram.CreateClass;
import artefact.umlClassDiagram.CreateGeneralization;
import artefact.umlClassDiagram.CreateOperation;
import artefact.umlClassDiagram.CreateOperationRefinement;
import artefact.umlClassDiagram.CreatePackage;
import artefact.umlClassDiagram.impl.CreateAttributeImpl;

import fr.lip6.meta.ple.constraintsextraction.constraint.Constraint;
import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.featureIdentification.Product;
import fr.lip6.meta.ple.generation.strategy.ProductLineConstructionStrategy;
import fr.lip6.meta.ple.generation.strategy.ReadXmiStrategy;
import fr.lip6.meta.ple.generation.tools.PLConstructionUtil;
import fr.lip6.meta.tools.StatementPrimitive;
import fr.lip6.meta.tools.Trigger;

/**
 * Features generation to AspectJ
 *
 */
public class ProductLineConstructionAJ implements ProductLineConstructionStrategy {
	private static String rootDir = "./SPL-Construction-AJ/";
	private static boolean indentXMLModel = true;
	private PLConstructionFactoryAJ factory = new PLConstructionFactoryAJ(rootDir+"src");
	private Collection<Product> products = null;
	//private HashMap<String, ArrayList<Trigger>> refineClasses;
	//private HashMap<String, ArrayList<Trigger>> newClassesFeatures;

	public ProductLineConstructionAJ() {
		File outputDir = new File (rootDir);
		File outputFeaturesRootDir = new File(factory.getSrcDir());
		
		PLConstructionUtil.deleteRecursive(outputDir);
		outputDir.mkdirs();
		outputFeaturesRootDir.mkdirs();
	}
	
	/* (non-Javadoc)
	 * @see fr.lip6.meta.ple.generation.strategy.ProductLineConstructionStrategy#generateFeatureModel(java.util.Collection, java.util.ArrayList)
	 */
	@Override
	public void generateFeatureModel(Collection<Feature> features, ArrayList<Constraint> rules) {
		PrintStream out;
		File dotFile = new File(rootDir+"model.xml");
		FileOutputStream fout;
		IndentHandler indentHandler;
		String xml = "";
		
		try {
			fout = new FileOutputStream(dotFile);
			out = new PrintStream(fout);
			indentHandler = new IndentHandler(out);
			
			xml += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+"\n";
			xml += "<featureModel>"+"\n";
			xml += "<struct>"+"\n";
			xml += "<and abstract=\"true\" mandatory=\"true\" name=\"SPLConstructionAJ\">"+"\n";

			for (Feature parentFeat : features) {
				String parentFeatName[] = parentFeat.getId().split("_");
				boolean isParentFeatMandatory = PLConstructionUtil.isMandatory(parentFeat, products);
				
				System.out.println("generateFeatureModel(): feature "+parentFeat.getId()+": "+parentFeat);
				
				if (parentFeatName.length == 1) {
					ArrayList<Feature> subFeatLvl1 = PLConstructionUtil.getSubfeaturesLevel1(parentFeat.getId(), features);
					
					if (subFeatLvl1.size() > 0) {
						if (isParentFeatMandatory) {
							xml += "<and abstract=\"true\" mandatory=\"true\" name=\""+parentFeat.getId()+"\""+" >"+"\n";
						} else {
							xml += "<and abstract=\"true\" name=\""+parentFeat.getId()+"\""+" >"+"\n";
						}
						
						for(Feature f1 : subFeatLvl1) {
							//String packageName = completePackageName(getFeaturePackageName(parentFeat));
							ArrayList<Feature> subFeatLvl2 = PLConstructionUtil.getSubfeaturesLevel2(f1.getId(), features);
							boolean isF1Mandatory = PLConstructionUtil.isMandatoryChild(f1, parentFeat, products);
							
							if (subFeatLvl2.size() > 0) {
								xml	+=	"<alt abstract=\"true\" "
									+	(isF1Mandatory ? "mandatory=\"true\" " : "")
									+	"name=\""+f1.getName()+"\" >"+"\n";
								
								for(Feature f2 : subFeatLvl2) {
									xml += getFeatureLvl2XML(	f2,
																true,
																PLConstructionUtil.isMandatoryChild(f2, f1, products)	);
								}
								
								xml	+=	"</alt>"+"\n";
							} else {
								xml	+=	"<feature "
									+	(isF1Mandatory ? "mandatory=\"true\" " : "")
									+	"name=\""+f1.getName()+"\" />"+"\n";
							}
						}
						xml += "</and>"+"\n";
					} else {
						xml += getFeatureLvl0XML(	parentFeat,
													false,
													isParentFeatMandatory);
					}
				}
			}
			
			xml += "</and>"+"\n";
			xml += "</struct>"+"\n";
			xml += "<constraints>"+"\n";

			System.out.println("####### FEATURE MODEL CONSTRAINTS #######\n"+rules.size()+" rules");
			
			for(Constraint rule : rules) {
				xml += "<rule>"+"\n";
				xml += "<eq>"+"\n";
				
				xml += rule.getLeft().toXML()+"\n";
				
				xml += rule.getRight().toXML()+"\n";
				xml += "</eq>"+"\n";
				xml += "</rule>"+"\n";
			}
			
			xml += "</constraints>"+"\n";
			xml += "<comments/>"+"\n";

			xml += "</featureModel>"+"\n";
			
			// indentation of the feature model xml code
			if (indentXMLModel)
				indentHandler.indentXml(xml, out);
			else
				out.print(xml);
		} catch (FileNotFoundException e) {
			System.err.println("generateFeatureModel(): "+e.getMessage());
		}
	}
	
	/* (non-Javadoc)
	 * @see fr.lip6.meta.ple.generation.strategy.ProductLineConstructionStrategy#construct(java.util.Collection, java.util.Collection)
	 */
	@Override
	public void construct(Collection<Feature> features, Collection<Product> products) {
		try {
			for(Feature feat : features) {
				System.out.println("\nProductLineConstructionAJ.construct(): FEATURE "+feat.getId()+":\n"+feat+"\n");
				generate(feat);
			}
		} catch (FileNotFoundException e) {
			System.err.println(e.getMessage());
		}
	}
	
	public void generate(Feature feat) throws FileNotFoundException {
		ArrayList<String> featClasses = new ArrayList<String>();
		//ArrayList<String> attributsName = new ArrayList<String>();
		//ArrayList<String> operationsName = new ArrayList<String>();
		//File featDir;
		String featName;

		if (feat.getId().split("_").length > 1) {
			featName = feat.getName();
		} else {
			featName = feat.getId();
		}
		
		System.out.println("ProductLineConstructionAJ.generate(): "+featName);

		//featDir = new File(factory.getSrcDir()+"/"+featName);
		//featDir.mkdir();
		
		for(Trigger trigger : feat) {
			StatementPrimitive stPrimitive = (StatementPrimitive) trigger;
			ConstructionPrimitive consPrimitive = stPrimitive.getPrimitive();
			@SuppressWarnings("unused")
			PrintStream out;
			
			System.out.println("Package base classes: "+PLConstructionFactoryAJ.packageBaseClasses.keySet());
			System.out.println("Feature classes: "+featClasses);
			System.out.println("Feature: "+featName+" ; construction primitive: "+consPrimitive);
			
			/*
			 * stPrimitive.getPrimitive() (ConstructionPrimtive) instances (7):
			 */

			// artefact.umlClassDiagram.CreatePackage
			if (consPrimitive instanceof CreatePackage) {
				System.out.println("[[ProductLineConstructionAJ.generate(): CREATE PACKAGE");
				out = factory.createPackage(stPrimitive, feat, featClasses);
				System.out.println("ProductLineConstructionAJ.generate(): CREATE PACKAGE]]]");
			}

			// artefact.umlClassDiagram.CreateClass
			if (consPrimitive instanceof CreateClass) {
				CreateClass crClass = (CreateClass) consPrimitive;
				
				if (!(featClasses.contains(crClass.getName()))) {
					System.out.println("[[ProductLineConstructionAJ.generate(): CREATE CLASS");
					out = factory.createClass(crClass, feat, null);
					System.out.println("ProductLineConstructionAJ.generate(): CREATE CLASS]]]");
				}
			}

			// artefact.umlClassDiagram.CreateGeneralization (inheritance)
			if (consPrimitive instanceof CreateGeneralization) {
				CreateGeneralization crGen = (CreateGeneralization) consPrimitive;
				
				if (!(featClasses.contains(crGen.getSub()))) {
					System.out.println("[[ProductLineConstructionAJ.generate(): CREATE GENERALIZATION");
					out = factory.createGeneralization(crGen, feat, null);
					System.out.println("ProductLineConstructionAJ.generate(): CREATE GENERALIZATION]]]");
				}
			}
			
			/*
			 * aspects possibles -->
			 */
			
			// artefact.umlClassDiagram.CreateAttribute
			if (consPrimitive instanceof CreateAttribute) {
				CreateAttributeImpl crAttr = (CreateAttributeImpl) consPrimitive;
				
				if (!(featClasses.contains(crAttr.getOwner()))) {
					System.out.println("[[ProductLineConstructionAJ.generate(): CREATE ATTRIBUTE");
					out = factory.createAttribute(crAttr, feat, featClasses);
					System.out.println("ProductLineConstructionAJ.generate(): CREATE ATTRIBUTE]]]");
				}
			}

			// artefact.umlClassDiagram.CreateOperation
			if (consPrimitive instanceof CreateOperation) {
				CreateOperation crOp = (CreateOperation) consPrimitive;
				
				if (!(featClasses.contains(crOp.getOwener()))) {
					System.out.println("[[ProductLineConstructionAJ.generate(): CREATE OPERATION");
					out = factory.createOperation(crOp, feat, featClasses);
					System.out.println("ProductLineConstructionAJ.generate(): CREATE OPERATION]]]");
				}
			}
			
			/*
			 * <-- aspects possibles
			 */

			// artefact.umlClassDiagram.CreateOperationRefinement
			if (consPrimitive instanceof CreateOperationRefinement) {
				CreateOperationRefinement crOpRef = (CreateOperationRefinement) consPrimitive;
				
				if (!(featClasses.contains(crOpRef.getOwner()))) {
					System.out.println("[[ProductLineConstructionAJ.generate(): CREATE OPERATION REFINEMENT");
					out = factory.createOperationRefinement(crOpRef, feat, featClasses);
					System.out.println("ProductLineConstructionAJ.generate(): CREATE OPERATION REFINEMENT]]]");
				}
			}
			
			// artefact.umlClassDiagram.CreateAssociation
			if (consPrimitive instanceof CreateAssociation) {
				//CreateAssociation crAsso = (CreateAssociation) consPrimitive;
				
				System.out.println("[[ProductLineConstructionAJ.generate(): CREATE ASSOCIATION");
				//out = factory.createAssociation(crAsso, feat, classesName);
				System.out.println("ProductLineConstructionAJ.generate(): CREATE ASSOCIATION]]]");
			}
			
			if (factory.getPrintFile() != null) {
				factory.getPrintFile().getOut().close();
				factory.setPrintFile(null);
			}
		}
	}
	
	public String getFeatureLvl0XML(	Feature feat,
										boolean isAbstract,
										boolean isMandatory	) {
		String xml = "";

		if (factory.featuresAspects.containsKey(feat)) {
			ArrayList<String> aspects = factory.featuresAspects.get(feat);
			
			xml += "<and ";
			xml += isAbstract ? "abstract=\"true\" " : "";
			xml += isMandatory ? "mandatory=\"true\" " : "";
			xml += "name=\""+feat.getId()+"\" >"+"\n";
			
			for(String aspect : aspects) {
				xml += "<feature mandatory=\"true\" name=\""+aspect+"\""+" />"+"\n";
			}
			xml += "</and>"+"\n";
		} else {
			xml += "<feature ";
			xml += isAbstract ? "abstract=\"true\" " : "";
			xml += isMandatory ? "mandatory=\"true\" " : "";
			xml += "name="+"\""+feat.getId()+"\""+" />"+"\n";
		}

		return xml;
	}
	
	public String getFeatureLvl2XML(	Feature feat,
									boolean isAbstract,
									boolean isMandatory	) {
		String xml = "";
		
		if (factory.featuresAspects.containsKey(feat)) {
			ArrayList<String> aspects = factory.featuresAspects.get(feat);
			
			xml += "<and ";
			xml += isAbstract ? "abstract=\"true\" " : "";
			xml += isMandatory ? "mandatory=\"true\" " : "";
			xml += "name=\""+feat.getName()+"\" >"+"\n";
			
			for(String aspect : aspects) {
				xml += "<feature mandatory=\"true\" name=\""+aspect+"\""+" />"+"\n";
			}
			xml += "</and>"+"\n";
		} else {
			xml += "<feature ";
			xml += isMandatory ? "mandatory=\"true\" " : "";
			xml += "name=\""+feat.getName()+"\" />"+"\n";
		}
		
		return xml;
	}
	
	/**
	 * AspectJ
	 * @param feat
	 * @return
	 */
	public static String getFeaturePackageName(Feature feat) {
		String pckg = "";
		
		for(Trigger trigger : feat) {
			ConstructionPrimitive consPrimitive = ((StatementPrimitive) trigger).getPrimitive();
			
			if (consPrimitive instanceof CreatePackage) {
				CreatePackage createPackage = (CreatePackage) consPrimitive;
				
				pckg = createPackage.getName();
				break;
			}
		}
		
		return pckg.replace('.', '_');
	}
	
	/**
	 * AspectJ
	 * @param pName
	 * @return name of the package, ending with '_' if it doesn't correspond to the empty string (default package)
	 */
	public static String completePackageName(String pName) {
		return pName.equals("") ? pName : pName+"_";
	}
	
	/**
	 * AspectJ
	 * @param fName
	 * @return name of the feature in accordance with AspectJ conventions
	 */
	public static String transformFeatureName(String fName) { // decomposition of String.replaceAll(regex, replacement)
		StringBuilder sb = new StringBuilder();
		char[] ca = fName.toCharArray();
		int l = ca.length, i = 0;
		
		while(i < l) {
			if (ca[i] == '_') {
				if (i < l-1) {
					sb.append(Character.toUpperCase(ca[i+1]));
					i++;
				}
			} else {
				sb.append(ca[i]);
			}
			i++;
		}

		return sb.toString();
	}
	
	// getters and setters
	
	/**
	 * @return
	 */
	public boolean isIndentXMLModel() {
		return indentXMLModel;
	}

	/**
	 * @param indentXMLModel
	 */
	public void setIndentXMLModel(boolean indentXMLModel) {
		ProductLineConstructionAJ.indentXMLModel = indentXMLModel;
	}
	
	/**
	 * @return
	 */
	public Collection<Product> getProducts() {
		return products;
	}

	/**
	 * @param products
	 */
	@Override
	public void setProducts(Collection<Product> products) {
		this.products = products;
	}
	
	/**
	 * @param readXmi
	 */
	public void setReadXmi(ReadXmiStrategy readXmi) {
		this.factory.setReadXmi(readXmi);
	}
	
	/**
	 * @author Simon Grandsire
	 * Indents XML code
	 */
	class IndentHandler extends DefaultHandler {
		protected String indent;
		protected PrintStream out;
		protected boolean varTag = false;

		public IndentHandler (PrintStream out) {
			indent = "";
			this.out = out;
		}

		public void startDocument () throws SAXException {
			out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
		}

		public void startElement (String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			
			out.print(indent + "<" + qName);
			
			for (int i=0, lg=attributes.getLength(); i<lg; ++i)
				out.print(" " + attributes.getQName(i) + "=\"" + attributes.getValue(i) + "\"");
			
			if (qName.equalsIgnoreCase("feature")) {
				out.println(" />");
			} else {
				if (qName.equalsIgnoreCase("var"))
					out.print(">");
				else
					out.println(">");
			}
			
			indent += "\t";
		}

		public void endElement (String uri, String localName, String qName)
				throws SAXException {
			
			indent = indent.substring(0, indent.length() - 1);
			
			if (!qName.equalsIgnoreCase("feature")) {
				if (qName.equalsIgnoreCase("var"))
					out.println(/*indent + */"</" + qName + ">");
				else
					out.println(indent + "</" + qName + ">");
			}
		}

		public void characters (char[] ch, int start, int length)
				throws SAXException {
			
			String s = new String(ch, start, length);
			
			if (!s.matches("^\\s*$"))
				out.print(/*indent + */s.trim());
		}

		public void fatalError (SAXParseException e) throws SAXException {
			System.err.println("\n***** Erreur fatale à la ligne " +
					e.getLineNumber() + ", \tau colonne " +
					e.getColumnNumber());
		}

		public void error (SAXParseException e) throws SAXException {
			System.err.println("\n***** Erreur non fatale à la ligne " +
					e.getLineNumber() + ", \tau colonne " +
					e.getColumnNumber());
		}
		
		public void indentXml (String xml, PrintStream out) {
			IndentHandler handler = new IndentHandler(out);

			try {
				XMLReader xmlReader = XMLReaderFactory.createXMLReader();

				xmlReader.setContentHandler(handler);
				xmlReader.setErrorHandler(handler);

		        InputStream xmlStream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
		        xmlReader.parse(new InputSource(xmlStream));
			} catch (SAXException e) {
				System.err.println("Problème(s) SAX : " + e.getMessage());
				System.exit(0);
			} catch (IOException e) {
				System.err.println("Problème(s) d'entrée : " +
						e.getMessage());
				System.exit(0);
			}
		}
	}
	
	/*public String getFeatureXml(	Feature feat,
									boolean mandatory,
									boolean abstract_	) {
		String xml = "";
		ArrayList<Trigger> aspects = findAspects(feat);
		
		if (aspects.size() > 0) {
			for(Trigger aspect : aspects) {
				StatementPrimitive stPrimitive = (StatementPrimitive) aspect;
				
				if (stPrimitive.getPrimitive() instanceof CreateAttribute) {
					CreateAttribute crAttr = (CreateAttribute) stPrimitive.getPrimitive();
					
					xml += "<feature name="+"\""+crAttr.getOwner()+"\""+" />"+"\n";
				} else {
					CreateOperation crOp = (CreateOperation) stPrimitive.getPrimitive();
					xml += "<feature name="+"\""+crOp.getOwener()+"\""+" />"+"\n";
				}
			}
		} else {
			xml += "<feature ";
			xml += mandatory ? "mandatory=\"true\" " : "";
			xml += abstract_ ? "abstract=\"true\" " : "";
			xml += "name="+"\""+feat.getId()+"\""+" />"+"\n";
		}

		return xml;
	}

	public ArrayList<Trigger> findAspects(Feature feat) {
		ArrayList<Trigger> aspects = new ArrayList<Trigger>();
		
		if (feat != factory.getBaseFeature()) {
			for(Trigger tr : feat) {
				StatementPrimitive stPrimitive = (StatementPrimitive) tr;
				
				if (	stPrimitive.getPrimitive() instanceof CreateAttribute
						||	stPrimitive.getPrimitive() instanceof CreateOperation	) {
					aspects.add(tr);
				}
			}
		}

		return aspects;
	}*/

	/*private void handleBaseFeature(Feature baseFeat) {
		System.out.println("BASE: "+baseFeat.getId());

		ArrayList<CreatePackage> packs = findPackages(baseFeat);

		System.out.println("Packages: "+packs.size());

		for(CreatePackage crPckg : packs) {
			HashMap<String, ArrayList<Trigger>> classes = findClasses(baseFeat, crPckg);
			System.out.println("Classes: "+classes);
			factory.setBaseClasses(classes);
			try {
				generateBase(crPckg, classes);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void generateBase(	CreatePackage crPckg,
								HashMap<String, ArrayList<Trigger>> classes) throws FileNotFoundException {
		PrintStream out;
		String path;
		File repit;
		Feature baseFeature = factory.getBaseFeature();

		if (baseFeature.getId().split("_").length > 1) {
			path = factory.getSrcDir()+"/"+baseFeature.getName()+"/";
		} else {
			path = factory.getSrcDir()+"/"+baseFeature.getId()+"/";
		}
		
		repit = new File (path);

		PLConstructionUtil.deleteRecursive(repit);
		repit.mkdirs();
		//print each class
		for(String cl : classes.keySet()) {
			File dotFile = new File(path+cl+".java");
			FileOutputStream fout;
			
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				//out.print("package "+p.getName()+";\n");
				System.out.print("package "+crPckg.getName()+";\n\n");
				out.print("public class "+cl+" {"+"\n");

				for(Trigger t : classes.get(cl)) {
					StatementPrimitive st = (StatementPrimitive) t;
					
					if (st.getPrimitive() instanceof CreateAttribute) {
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute) st.getPrimitive();
						out.println("\t"+ca.getType()+"  "+ca.getName()+";"); 
						System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}

					if (st.getPrimitive() instanceof CreateOperation){
						System.out.println("PRINT Operation");
						CreateOperation co = (CreateOperation)st.getPrimitive();
						out.println("\tpublic void "+co.getName()+"() {\n");
						System.out.println("public void "+co.getName()+"() {\n");
						out.println("\t}");
						System.out.println("}");
					}
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void handleOptionalFeatures(	Collection<Feature> features,
											Collection<Product> products	) {
		for(Feature feat : features) {
			if (feat != factory.getBaseFeature()) {
				HashMap<String, ArrayList<Trigger>> refineCls = new HashMap<String, ArrayList<Trigger>>();
				HashMap<String, ArrayList<Trigger>> newClsFeats = new HashMap<String, ArrayList<Trigger>>();

				for(Trigger t : feat) {
					StatementPrimitive st = (StatementPrimitive) t;
					
					System.out.println("Trigger :"+t.getClass());

					if (st.getPrimitive() instanceof CreateClass) {
						CreateClass crClass = (CreateClass) st.getPrimitive();
						
						if (!newClsFeats.containsKey(crClass.getName()))
							newClsFeats.put(crClass.getName(), new ArrayList<Trigger>());
					} else {
						if (st.getPrimitive() instanceof CreateAttribute) {
							CreateAttribute ca = (CreateAttribute) st.getPrimitive();
							
							System.out.println("CreateAttribute :"+ca.getName());
							
							if (factory.getBaseClasses().containsKey(ca.getOwner())){
								if (refineCls.containsKey(ca.getOwner())){
									refineCls.get(ca.getOwner()).add(t);
								} else {
									ArrayList<Trigger> tab	=new ArrayList<Trigger>();
									tab.add(t);
									refineCls.put(ca.getOwner(), tab);
								}
							} else { // new class
								if (newClsFeats.containsKey(ca.getOwner())){
									newClsFeats.get(ca.getOwner()).add(t);
								} else {
									ArrayList<Trigger> tab	=new ArrayList<Trigger>();
									tab.add(t);
									newClsFeats.put(ca.getOwner(), tab);
								}
							}
						} else {
							if (st.getPrimitive() instanceof CreateOperation){
								CreateOperation co = (CreateOperation) st.getPrimitive();
								
								System.out.println("CreateOperation :"+co.getName());

								if (factory.getBaseClasses().containsKey(co.getOwener())) {
									if (refineCls.containsKey(co.getOwener())) {
										refineCls.get(co.getOwener()).add(t);
									} else {
										ArrayList<Trigger> tab	=new ArrayList<Trigger>();
										tab.add(t);
										refineCls.put(co.getOwener(), tab);
									}
								} else { // new class
									if(newClsFeats.containsKey(co.getOwener())){
										newClsFeats.get(co.getOwener()).add(t);
									} else {
										ArrayList<Trigger> tab	=new ArrayList<Trigger>();
										tab.add(t);
										newClsFeats.put(co.getOwener(), tab);
									}
								}
							}
						}
					}
				} // for

				//generateOptionalFeatures(path, feat, refineCls, newClassesFeatures);
			}
		}
	}
	
	private void generateOptionalFeatures(	String path,
													Feature f,
													HashMap<String, ArrayList<Trigger>> refineClasses,
													HashMap<String, ArrayList<Trigger>> newClassesFeatures	) {
		// traitement new classes
		
		PrintStream out;
		String rep = path+f.getId()+"/";
		File repit = new File (rep);
		
		PLConstructionUtil.deleteRecursive(repit);
		repit.mkdirs();
		// print each class
		for(String cl : newClassesFeatures.keySet()){
			File dotFile =new File(rep+cl+".jak");
			FileOutputStream fout;
			
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				
				out.print("public class "+cl+" {"+"\n");

				for(Trigger t : newClassesFeatures.get(cl)){
					StatementPrimitive st = (StatementPrimitive) t;
					
					if (st.getPrimitive() instanceof CreateAttribute) {
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					    out.println(ca.getType()+"  "+ca.getName()+";"); 
					    System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}
					
					if (st.getPrimitive() instanceof CreateOperation) {
						System.out.println("PRINT Operation");
						CreateOperation co = (CreateOperation) st.getPrimitive();
						out.println("public void "+co.getName()+"() {\n");
						System.out.println("public void "+co.getName()+"() {\n");
						out.println("}");
						System.out.println("}");
					}
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		///////-------------
		
		//  traitement refineclasses
		
		//repit = new File (path+f.getId());
		//deleteRecursive(repit);
		//repit.mkdirs();
		//print each class
		for(String cl : refineClasses.keySet()) {
			File dotFile =new File(rep+cl+".jak");
			FileOutputStream fout;
		
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				
				out.print("public refines class "+cl+" {"+"\n");
				
				
				for(Trigger t : refineClasses.get(cl)) {
					StatementPrimitive st = (StatementPrimitive) t;
					if (st.getPrimitive() instanceof CreateAttribute) {
						
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					    out.println(ca.getType()+"  "+ca.getName()+";"); 
					    System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}
					
					if (st.getPrimitive() instanceof CreateOperation) {
						System.out.println("PRINT Operation");
						CreateOperation co= (CreateOperation)st.getPrimitive();
						out.println("public void "+co.getName()+"() {\n");
						System.out.println("public void "+co.getName()+"() {\n");
						out.println("}");
						System.out.println("}");
					}
					
					
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	private static ArrayList<CreatePackage> findPackages(ArrayList<Trigger> set) {
		ArrayList<CreatePackage> result = new ArrayList<CreatePackage>();
		
		for(Trigger t: set){
			//System.out.println("Trigger :"+t.getClass());
			StatementPrimitive st = (StatementPrimitive)t;
			
			if (st.getPrimitive() instanceof CreatePackage){
				result.add((CreatePackage)st.getPrimitive());
			}
		}
		
		return result;
	}
	
	private static HashMap<String, ArrayList<Trigger>> findClasses(	ArrayList<Trigger> triggers,
																	CreatePackage crPckg	) {
		HashMap<String, ArrayList<Trigger>> result = new HashMap<String, ArrayList<Trigger>>();
		
		//System.out.println("Feature:"+triggers);
		
		for(Trigger t : triggers) {
			StatementPrimitive st = (StatementPrimitive) t;
			//System.out.println("Trigger: "+t.getClass());

			if (st.getPrimitive() instanceof CreateClass) {
				CreateClass cl = (CreateClass) st.getPrimitive();
				
				//System.out.println("CreateClass: "+cl.getName());

				if (cl.getOwener().equals(crPckg.getName())) {
					if (!result.containsKey(cl.getName()))
						result.put(cl.getName(), new ArrayList<Trigger>());
				}
			} else {
				if (st.getPrimitive() instanceof CreateAttribute) {		
					CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					
					//System.out.println("CreateAttribute: "+ca.getName());
					if (result.containsKey(ca.getOwner())) {
						result.get(ca.getOwner()).add(t);
					} else {
						ArrayList<Trigger> tab	=new ArrayList<Trigger>();
						
						tab.add(t);
						result.put(ca.getOwner(), tab);
					}
				} else {
					if (st.getPrimitive() instanceof CreateOperation) {
						CreateOperation co = (CreateOperation)st.getPrimitive();
						
						//System.out.println("CreateOperation: "+co.getName());
						
						if (result.containsKey(co.getOwener())) {
							result.get(co.getOwener()).add(t);
						} else {
							ArrayList<Trigger> tab	=new ArrayList<Trigger>();
							
							tab.add(t);
							result.put(co.getOwener(), tab);
						}
					}
				}
			}
		}
		
		return result;
	}
	*/
}